package heliotrope.game.system
{
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.geom.Point;
	
	import heliotrope.game.GameVar;
	import heliotrope.game.character.Player;
	import heliotrope.game.scene.map.Map;
	import heliotrope.global.GlobalVar;

	public class RenderSystem
	{
		private static var __instance:RenderSystem;
		private static var __FACT = 0.618;
		public var forceSet:Boolean;
		public var map:Map;
		public var focusplayer:Player;
		private var _gameVar:GameVar;
		private var _stage:Stage;
		public function RenderSystem(pvt:PrivateClass)
		{
			_stage = GlobalVar.getInstance().stage;
			_gameVar = GameVar.getInstance();
			forceSet = false;
		}
		private function render():void
		{
			if(!forceSet)
			{
				displayUpdate(_gameVar.playerPos);
			}
			else
			{
				var tmp:Number = __FACT;
				__FACT = 0;//Expand fence to full screen.
				displayUpdate(_gameVar.playerPos);
				__FACT = tmp;
				forceSet = false;
			}
			map.update();
		}
		private function displayUpdate(playerPos_:Point):void
		{
			var stageWidth:int = _stage.stageWidth;
			var stageHeight:int = _stage.stageHeight;
			var mapWidth:Number = map.width;
			var mapHeight:Number = map.height;
			var fenceLeft:Number = stageWidth*(1-__FACT)*0.5;
			var fenceRight:Number = stageWidth*0.5*(1+__FACT);
			var fenceTop:Number = stageHeight*(1-__FACT)*0.5;
			var fenceBottom:Number = stageHeight*0.5*(1+__FACT);
			var localX:Number = playerPos_.x;
			var localY:Number = playerPos_.y;
			var stagePoint:Point = map.localToGlobal(new Point(localX, localY));
			
			var resultX:Array = axisCalcu(stagePoint.x, localX, map.x, mapWidth, stageWidth, focusplayer.x, fenceLeft, fenceRight);
			var resultY:Array = axisCalcu(stagePoint.y, localY, map.y, mapHeight, stageHeight, focusplayer.y, fenceTop, fenceBottom);
			focusplayer.x = resultX[0];map.x = resultX[1];
			focusplayer.y = resultY[0];map.y = resultY[1];
		}
		
		private function axisCalcu(viewCoo_:Number, axisCoo_:Number, axisPos_:Number,
								   axisLength_:Number, viewPortSize_:Number, focusPos_:Number,
								   fenceLow_:Number, fenceUp_:Number, lowBounds_:Number=0, upBounds_:Number=Number.MAX_VALUE):Array
		{
			lowBounds_ = lowBounds_<0? 0:lowBounds_;
			upBounds_ = upBounds_>axisLength_? axisLength_:upBounds_;
			var inFence:Boolean = isInRange(viewCoo_, fenceLow_, fenceUp_);
			var boundLeft:Boolean	= isInRange(axisCoo_, lowBounds_, lowBounds_+fenceLow_);
			var boundRight:Boolean	= isInRange(axisCoo_, upBounds_-fenceLow_, upBounds_);
			var result:Array = [];
			result[0] = focusPos_;
			result[1] = axisPos_;
			if(inFence)
			{
				result[0] = viewCoo_;
			}
			else if(boundLeft)
			{
				result[0] = axisCoo_-lowBounds_;
				result[1] = -lowBounds_;
			}
			else if(boundRight)
			{
				result[0] = axisCoo_-upBounds_+viewPortSize_;
				result[1] = viewPortSize_-upBounds_;
			}
			else//not in fence&not close to bounds
			{
				var toLeft:Boolean = isInRange(viewCoo_, -Number.MAX_VALUE, viewPortSize_/2);
				var toRight:Boolean = !toLeft;
				if(toLeft)
				{
					result[0] = viewPortSize_-fenceUp_;
				}
				if(toRight)
				{
					result[0] = fenceUp_;
				}
				result[1] = result[0]-axisCoo_;
			}
			return result;
		}
		
		private function isInRange(target_:Number, left:Number=-Number.MAX_VALUE, right_:Number=Number.MAX_VALUE):Boolean
		{
			return (target_<=right_)&&(target_>=left);
		}
		public static function init():void
		{
			if(RenderSystem.__instance)
			{
				throw new Error("You can't init the RenderSystem twice!")
			}
			RenderSystem.__instance = new RenderSystem(new PrivateClass);
		}
		
		public static function getInstance():RenderSystem
		{
			return RenderSystem.__instance;
		}
	}
}

class PrivateClass
{
	public function PrivateClass()
	{
	}
}